package com.gitee.wsl.transform.encode.pinyin.trie

import kotlin.jvm.JvmOverloads

/**
 *
 *
 * A state has various important tasks it must attend to:
 *
 *
 *  * success; when a character points to another state, it must return that state
 *  * failure; when a character has no matching state, the algorithm must be able to fall back on a
 * state with less depth
 *  * emits; when this state is passed and keywords have been matched, the matches must be
 * 'emitted' so that they can be used later on.
 *
 *
 *
 * The root state is special in the sense that it has no failure state; it cannot fail. If it 'fails'
 * it will still parse the next character and start from the root node. This ensures that the algorithm
 * always runs. All other states always have a fail state.
 *
 *
 * @author Robert Bor
 */
class State @JvmOverloads constructor(
    /**
     * effective the size of the keyword
     */
    val depth: Int = 0
) {
    /**
     * only used for the root state to refer to itself in case no matches have been found
     */
    private val rootState: State? = if (depth == 0) this else null

    /**
     * referred to in the white paper as the 'goto' structure. From a state it is possible to go
     * to other states, depending on the character passed.
     */
    private val success: MutableMap<Char, State> = HashMap()

    /**
     * if no matching states are found, the failure state will be returned
     */
    var failure: State? = null

    /**
     * whenever this state is reached, it will emit the matches keywords for future reference
     */
    private var emits: MutableSet<String>? = null

    private fun nextState(character: Char, ignoreRootState: Boolean): State? {
        var nextState = this.success[character]

        if (!ignoreRootState && nextState == null && this.rootState != null) {
            nextState = this.rootState
        }

        return nextState
    }

    fun nextState(character: Char): State? {
        return nextState(character, false)
    }

    fun nextStateIgnoreRootState(character: Char): State? {
        return nextState(character, true)
    }

    fun addState(keyword: String): State {
        var state = this

        for (character in keyword.toCharArray()) {
            state = state.addState(character)
        }

        return state
    }

    fun addState(character: Char): State {
        var nextState = nextStateIgnoreRootState(character)
        if (nextState == null) {
            nextState = State(this.depth + 1)
            this.success.put(character, nextState)
        }
        return nextState
    }

    fun addEmit(keyword: String) {
        if (this.emits == null) {
            this.emits = mutableSetOf()
        }
        this.emits!!.add(keyword)
    }

    fun addEmit(emits: Collection<String>) {
        for (emit in emits) {
            addEmit(emit)
        }
    }

    fun emit(): Collection<String> {
        return this.emits?:emptyList()
    }

//    fun failure(): State? {
//        return this.failure
//    }
//
//    fun setFailure(failState: State?) {
//        this.failure = failState
//    }

    val states: Collection<State>
        get() = this.success.values

    val transitions: Collection<Char>
        get() = this.success.keys
}